﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.Model
{
    public abstract class Doc3dEditRuntime : IDocumentEditor
    {
        protected WebGLRenderer renderer;
        protected Camera camera;
        protected Scene scene;
        protected RenderCanvas canvas = new RenderCanvas();
        protected Vector2 mouseLoc = new Vector2();
        public const int UnSelectChannel = 2;
        public string Name { get; protected set; }
        public EditorType EditorType { get; protected set; }
        public IDocument3dEditorControl Doc3dEditControl { get; protected set; }
        public DocumentRuntime DocRt { get; protected set; }
        public CommandCenter CommandCenter { get; protected set; }
        public Snap3dRuntime Snap3DRuntime { get; protected set; }
        public Doc3dEditRuntime(DocumentRuntime docRt, IDocument3dEditorControl control, ICommandControl commander)
        {
            this.DocRt = docRt;
            this.Doc3dEditControl = control;
            this.Doc3dEditControl.AttachEvents(this.OnInit, this.OnDestory, this.OnMouseEvent, this.OnKeyDown, this.OnKeyUp, this.OnSizeChanged, this.OnRender);
            this.CommandCenter = new CommandCenter(this, commander);
            this.Snap3DRuntime = new Snap3dRuntime(this);

        }
       
        public virtual Group GetExtGroup(string grpName)
        {

            var grp = scene.getObjectByName(grpName) as Group;
            if (grp != null)
                return grp;
            else
            {
                grp = new Group();
                grp.name = grpName;
                this.scene.add(grp);
                return grp;
            }
        }
        
        public virtual void CommandExecute(LcCommand cmd)
        {
        }

        public virtual void SetActive(bool active)
        {
            if (active)
                this.CommandCenter.SetActive();
            this.Doc3dEditControl.SetActive(active);
        }

        public abstract void EnableCameraControl(bool enable);

        public virtual void CancelAll()
        {
            this.CommandCenter.InAction = false;
            this.CommandCenter.ClearInput();
            this.CommandCenter.Commander.Prompt(string.Empty);
        }

        public virtual void Dispose()
        {
            this.Doc3dEditControl.DetachEvents(this.OnInit, this.OnDestory, this.OnMouseEvent, this.OnKeyDown, this.OnKeyUp, this.OnSizeChanged, this.OnRender);
        }
        protected virtual void OnInit()
        {
            this.canvas.Set((int)this.Doc3dEditControl.EditorBounds.Width, (int)this.Doc3dEditControl.EditorBounds.Height);
            this.scene = new Scene();
            var ambientLight = new AmbientLight(0xcccccc, 0.4);
            scene.add(ambientLight);
            var dirLight1 = new DirectionalLight(0xffffff, 0.8);
            dirLight1.position.Set(1, 2, 3);
            scene.add(dirLight1);
            var dirLight2 = new DirectionalLight(0xffffff, 0.8);
            dirLight2.position.Set(-1, -2, -3);
            scene.add(dirLight2);

            var ratio = canvas.Width / canvas.Height;
            camera = new OrthographicCamera(-canvas.Width / 2, canvas.Width / 2, canvas.Height / 2, -canvas.Height / 2, 0.1, 200000);
            camera.quaternion.OnChange(() =>
            {
                camera.rotation.SetFromQuaternion(camera.quaternion, camera.rotation.Order, true);
            });
            camera.rotation.OnChange(() =>
            {
                return;
                if (Math.Abs(camera.rotation.X - Math.PI / 2) < 1e-3)
                    camera.up.Set(0, -1, 0);
                else if (Math.Abs(camera.rotation.X + Math.PI / 2) < 1e-3)
                    camera.up.Set(0, 1, 0);
                else camera.up.Set(0, 0, 1);
                camera.updateProjectionMatrix();
            });
            camera.layers.enable(UnSelectChannel);
            camera.zoom = 0.05;
            camera.up.Set(0, 0, 1);
            camera.lookAt(new Vector3());

            var axesHelper = new AxesHelper(20000);
            axesHelper.layers.set(UnSelectChannel);
            scene.add(axesHelper);
            this.canvas = new RenderCanvas().Set(canvas.Width, canvas.Height);
            this.renderer = new WebGLRenderer(canvas);
        }

        protected virtual void OnDestory()
        {
            this.renderer.dispose();
        }

        protected virtual void OnSizeChanged(Bounds bounds)
        {
            this.renderer.setSize(bounds.Width, bounds.Height);
            //canvas.Set((int)bounds.Width, (int)bounds.Height);
            if (camera is PerspectiveCamera)
            {
                (camera as PerspectiveCamera).aspect = (double)this.canvas.Width / this.canvas.Height;
            }
            else
            {
                var oriCamera = camera as OrthographicCamera;
                oriCamera.left = -this.canvas.Width / 2.0;
                oriCamera.right = this.canvas.Width / 2.0;
                oriCamera.top = this.canvas.Height / 2.0;
                oriCamera.bottom = -this.canvas.Height / 2.0;
            }
            camera.updateProjectionMatrix();
        }

        protected virtual void OnMouseEvent(string type, MouseEventRuntime e)
        {
            if (type == "Down") OnMouseDown(e);
            else if (type == "Move") OnMouseMove(e);
            else if (type == "Up") OnMouseUp(e);
            else if (type == "Wheel") OnMouseWheel(e);
        }
        protected virtual void OnMouseDown(MouseEventRuntime e) { }

        protected virtual void OnMouseMove(MouseEventRuntime e)
        {
            var sx = (double)e.Location.X;
            var sy = (double)e.Location.Y;
            var width = this.Doc3dEditControl.EditorBounds.Width;
            var height = this.Doc3dEditControl.EditorBounds.Height;
            sx = sx * 2 / width - 1;
            sy = (height - sy) * 2 / height - 1;
            mouseLoc.Set(sx, sy);
        }

        protected virtual void OnMouseUp(MouseEventRuntime e) { }

        protected virtual void OnMouseWheel(MouseEventRuntime e) { }

        protected virtual void OnKeyDown(KeyEventRuntime e)
        {
            if (e.KeyCode == 17)//Ctrl
            {

            }
            else if (e.KeyCode == 27)//ESC
            {
            }

            if (e.KeyModifiers == LcKeyModifiers.Control)
            {

            }
            else if (e.KeyCode == 46)//Delete
            {
                this.CommandCenter.Execute("ERASE");
            }

            //处理在位输入及Esc/Copy，Cut/Paste将启动相应Action
        }

        protected virtual void OnKeyUp(KeyEventRuntime e)
        {
            if (e.KeyCode == 17)//Ctrl
            {
            }
        }

        protected virtual void OnRender()
        {
            this.renderer.setClearColor(0x222222);
            this.renderer.render(this.scene, camera);
        }

        public virtual ListEx<Raycaster.Intersection> RayCaster(Raycaster eleRaycaster, Action<Camera> cameraAction)
        {
            eleRaycaster.setFromCamera(mouseLoc, camera);
            cameraAction?.Invoke(camera);
            var intersects = eleRaycaster.intersectObject(this.scene, true);
            return intersects;
        }

    }
}
